using System.Linq;
using System.Collections.Generic;
using System.Net.Mime;
using System;
using System.Reflection;
using UnityEditor.Experimental.GraphView;
using UnityEditor.UIElements;
using UnityEngine;
using UnityEngine.UIElements;
using UnityEditor;

namespace Framework.Fsm
{
    public class FsmActionItem : Node
    {
        public FsmStateNode Node;
        public FsmUserData Data { get; private set; }
        public Type Type { get; private set; }
        private Button settingButton;
        VisualElement settings;
        FsmSettingView settingsContainer;
        bool settingsExpanded;
        private Dictionary<string, FsmDataActionParamData> _fields;
        private int paramCount;
        private FsmGraphContext _context;
        public FsmActionItem()
        {
            _fields = new Dictionary<string, FsmDataActionParamData>();
            this.RefreshPorts();
            Data = new FsmUserData() { Id = Guid.NewGuid().ToString(), DataType = UserDataType.Action, Target = this };
            // Add(new EnumField("Color", ProduceType.DropRes));
            // var input = new TextField();
            // input.value = "...";
            // Add(input);
            // Add(new IntegerField("Int"));
            // Add(new FloatField("Float"));
            // Add(new Vector2Field("Vector2"));
            // Add(new Vector3Field("Vector3"));
            // Add(new Vector4Field("Vector4"));
            // Add(new ColorField("Color"));
            settingButton = new Button(ToggleSettings) { name = "settings-button" };
            settingButton.style.width = settingButton.style.height = 30;
            settingButton.Add(new Image { name = "icon", scaleMode = ScaleMode.ScaleToFit, image = Resources.Load<Texture>("SettingsIcons") });

            titleContainer.Add(settingButton);
            RegisterCallback<GeometryChangedEvent>(OnGeometryChanged);

            settingsContainer = new FsmSettingView();
            settingsContainer.visible = false;
            settings = new VisualElement();
            settingsContainer.Add(settings);
            Add(settingsContainer);
            paramCount = 0;
        }
        public void Initialize(FsmGraphContext context)
        {
            _context = context;
        }
        public override void BuildContextualMenu(ContextualMenuPopulateEvent evt)
        {
            // base.BuildContextualMenu(evt);
            evt.menu.InsertAction(0, "编辑", (e) =>
            {
                var list = AssetDatabase.FindAssets(string.Format("{0} t:MonoScript", this.Type.ToString()));
                if (list.Length > 0)
                {
                    foreach (var item in list)
                    {
                        var path = AssetDatabase.GUIDToAssetPath(item);
                        UnityEngine.Object obj = AssetDatabase.LoadAssetAtPath(path, typeof(MonoScript));
                        if (obj != null)
                        {
                            AssetDatabase.OpenAsset(obj, 0);
                            break;
                        }
                    }
                }
            }, DropdownMenuAction.AlwaysEnabled);
        }
        public void Set(Type type, string title)
        {
            this.Type = type;
            if (string.IsNullOrEmpty(title))
            {
                title = type.ToString();
            }
            this.title = title;

            createSetting();
        }
        void ToggleSettings()
        {
            settingsExpanded = !settingsExpanded;
            if (settingsExpanded)
                OpenSettings();
            else
                CloseSettings();
        }

        public void OpenSettings()
        {
            // if (_fields.Count < 1) return;
            if (settingsContainer.childCount < 1) return;
            if (this.Node != null && _context != null)
            {
                _context.View.ClearSelection();
                _context.View.AddToSelection(this.Node);
            }
            if (settingsContainer != null)
            {
                // owner.ClearSelection();
                // owner.AddToSelection(this);

                settingButton.AddToClassList("clicked");
                settingsContainer.visible = true;
                settingsExpanded = true;
            }
        }

        public void CloseSettings()
        {
            if (settingsContainer != null)
            {
                settingButton.RemoveFromClassList("clicked");
                settingsContainer.visible = false;
                settingsExpanded = false;
            }
        }
        void OnGeometryChanged(GeometryChangedEvent evt)
        {
            if (settingButton != null)
            {
                var settingsButtonLayout = settingButton.ChangeCoordinatesTo(settingsContainer.parent, settingButton.layout);
                settingsContainer.style.top = settingsButtonLayout.yMax - 40f;
                settingsContainer.style.left = settingsButtonLayout.xMin - layout.width + 80f;
            }
        }
        public void Set(FsmDataActionData value)
        {
            var t = FsmProvider.GetTypeByName(value.Type);
            this.Type = t;
            var name = value.Name;
            if (string.IsNullOrEmpty(name))
            {
                name = t.ToString();
            }
            this.title = name;
            foreach (var item in value.Params)
            {
                _fields.Add(item.Name, item);
            }
            createSetting();
        }
        public FsmDataActionParamData[] GetParam()
        {
            var list = new List<FsmDataActionParamData>();
            foreach (var item in _fields)
            {
                list.Add(item.Value);
            }
            return list.ToArray();
        }
        private void createSetting()
        {
            if(this.Type == null)
            {
                return;
            }
            var fields = this.Type.GetFields(BindingFlags.Public | BindingFlags.Instance);
            var ins = System.Activator.CreateInstance(Type);
            foreach (var field in fields)
            {
                if (field.FieldType == typeof(string))
                {
                    paramCount++;
                    var f = new TextField(field.Name);
                    f.style.flexDirection = FlexDirection.Column;
                    f.RegisterValueChangedCallback(change =>
                    {
                        var v = change.newValue;
                        if (!_fields.ContainsKey(field.Name))
                        {
                            var p = new FsmDataActionParamData
                            {
                                Name = field.Name,
                            };
                            _fields.Add(field.Name, p);
                        }
                        _fields[field.Name].Set(v);
                    });
                    settingsContainer.Add(f);
                    if (_fields.ContainsKey(field.Name))
                    {
                        f.value = _fields[field.Name].GetString();
                    }
                    else
                    {
                        f.value = (string)field.GetValue(ins);
                    }
                }
                else if (field.FieldType == typeof(int))
                {
                    paramCount++;
                    var f = new IntegerField(field.Name);
                    f.style.flexDirection = FlexDirection.Column;
                    f.RegisterValueChangedCallback(change =>
                    {
                        var v = change.newValue;
                        if (!_fields.ContainsKey(field.Name))
                        {
                            var p = new FsmDataActionParamData
                            {
                                Name = field.Name,
                            };
                            _fields.Add(field.Name, p);
                        }
                        _fields[field.Name].Set(v);
                    });
                    settingsContainer.Add(f);
                    if (_fields.ContainsKey(field.Name))
                    {
                        f.value = _fields[field.Name].GetInt();
                    }
                    else
                    {
                        f.value = (int)field.GetValue(ins);
                    }
                }
                else if (field.FieldType == typeof(float))
                {
                    paramCount++;
                    var f = new FloatField(field.Name);
                    f.style.flexDirection = FlexDirection.Column;
                    f.RegisterValueChangedCallback(change =>
                    {
                        var v = change.newValue;
                        if (!_fields.ContainsKey(field.Name))
                        {
                            var p = new FsmDataActionParamData
                            {
                                Name = field.Name,
                            };
                            _fields.Add(field.Name, p);
                        }
                        _fields[field.Name].Set(v);
                    });
                    settingsContainer.Add(f);
                    if (_fields.ContainsKey(field.Name))
                    {
                        f.value = _fields[field.Name].GetFloat();
                    }
                    else
                    {
                        f.value = (float)field.GetValue(ins);
                    }
                }
                else if (field.FieldType == typeof(bool))
                {
                    paramCount++;
                    var f = new Toggle(field.Name);
                    f.style.flexDirection = FlexDirection.Column;
                    f.RegisterValueChangedCallback(change =>
                    {
                        var v = change.newValue;
                        if (!_fields.ContainsKey(field.Name))
                        {
                            var p = new FsmDataActionParamData
                            {
                                Name = field.Name,
                            };
                            _fields.Add(field.Name, p);
                        }
                        _fields[field.Name].Set(v);
                    });
                    settingsContainer.Add(f);
                    if (_fields.ContainsKey(field.Name))
                    {
                        f.value = _fields[field.Name].GetBool();
                    }
                    else
                    {
                        f.value = (bool)field.GetValue(ins);
                    }
                }
                else if (field.FieldType == typeof(Vector3))
                {
                    paramCount++;
                    var f = new Vector3Field(field.Name);
                    f.style.flexDirection = FlexDirection.Column;
                    f.RegisterValueChangedCallback(change =>
                    {
                        var v = change.newValue;
                        if (!_fields.ContainsKey(field.Name))
                        {
                            var p = new FsmDataActionParamData
                            {
                                Name = field.Name,
                            };
                            _fields.Add(field.Name, p);
                        }
                        _fields[field.Name].Set(v);
                    });
                    settingsContainer.Add(f);
                    if (_fields.ContainsKey(field.Name))
                    {
                        f.value = _fields[field.Name].GetVector3();
                    }
                    else
                    {
                        f.value = (Vector3)field.GetValue(ins);
                    }
                }
                else if (field.FieldType.IsEnum)
                {
                    paramCount++;
                    var f = new EnumField(field.Name);
                    var list = Enum.GetValues(field.FieldType).Cast<Enum>();
                    f.Init(list.First<Enum>());
                    f.style.flexDirection = FlexDirection.Column;
                    f.RegisterValueChangedCallback(change =>
                    {
                        var v = change.newValue;
                        if (!_fields.ContainsKey(field.Name))
                        {
                            var p = new FsmDataActionParamData
                            {
                                Name = field.Name,
                            };
                            _fields.Add(field.Name, p);
                        }
                        _fields[field.Name].Set(v);
                    });
                    settingsContainer.Add(f);
                    if (_fields.ContainsKey(field.Name))
                    {
                        f.value = (Enum)Enum.ToObject(field.FieldType, _fields[field.Name].GetInt());
                    }
                    else
                    {
                        f.value = (Enum)field.GetValue(ins);
                    }
                }
                else if (field.FieldType == typeof(Vector2))
                {
                    paramCount++;
                    var f = new Vector2Field(field.Name);
                    f.style.flexDirection = FlexDirection.Column;
                    f.RegisterValueChangedCallback(change =>
                    {
                        var v = change.newValue;
                        if (!_fields.ContainsKey(field.Name))
                        {
                            var p = new FsmDataActionParamData
                            {
                                Name = field.Name,
                            };
                            _fields.Add(field.Name, p);
                        }
                        _fields[field.Name].Set(v);
                    });
                    settingsContainer.Add(f);
                    if (_fields.ContainsKey(field.Name))
                    {
                        f.value = _fields[field.Name].GetVector2();
                    }
                    else
                    {
                        f.value = (Vector2)field.GetValue(ins);
                    }
                }

            }
            settingButton.visible = paramCount > 0;
        }
    }
}